# cost / budget - 只显示偏差部分
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

# 读取CSV文件
df = pd.read_csv('PerformanceTest_ReviseTaskLevel.csv')

# 提取所需列
data = df[['Recipe', 'TaskNumber', 'Algorithm', 'BudgetUsage']]

# 将BudgetUsage列转换为数值类型
data['BudgetUsage'] = pd.to_numeric(data['BudgetUsage'], errors='coerce')

# 检查是否有转换失败的值（变为NaN）
if data['BudgetUsage'].isna().any():
    print("警告: 部分BudgetUsage值无法转换为数字，已设为NaN")
    print(f"无法转换的值数量: {data['BudgetUsage'].isna().sum()}")

# 计算每个工作流中各个算法的预算使用率
BudgetUsage_stats = data.groupby(['Recipe', 'TaskNumber', 'Algorithm'])['BudgetUsage'].mean().reset_index()
BudgetUsage_stats['BudgetUsage_Rate'] = BudgetUsage_stats['BudgetUsage'] * 100

# 设置图形样式
plt.style.use('default')
palette = sns.color_palette("Set2")

# 获取所有工作流
recipes = data['Recipe'].unique()

# 为每个工作流创建预算使用率偏差图
for recipe in sorted(recipes):
    # 筛选当前工作流的数据
    recipe_data = BudgetUsage_stats[BudgetUsage_stats['Recipe'] == recipe]
    
    # 创建图形
    fig, ax = plt.subplots(figsize=(12, 8))
    
    # 获取当前工作流的所有算法
    algorithms = recipe_data['Algorithm'].unique()
    
    # 为每个算法设置颜色
    colors = {alg: palette[i % len(palette)] for i, alg in enumerate(algorithms)}
    
    # 计算合适的Y轴范围
    max_rate = recipe_data['BudgetUsage_Rate'].max()
    min_rate = recipe_data['BudgetUsage_Rate'].min()
    y_min = min(min_rate, 100) - 5  # 留5%的边距
    y_max = max(max_rate, 100) + 5
    
    # 绘制偏差图
    task_numbers = sorted(recipe_data['TaskNumber'].unique())
    
    # 为每个TaskNumber设置x位置
    x_positions = np.arange(len(task_numbers))
    bar_width = 0.8 / len(algorithms)  # 动态调整柱子宽度
    
    for i, algorithm in enumerate(algorithms):
        alg_data = recipe_data[recipe_data['Algorithm'] == algorithm]
        
        # 确保数据按TaskNumber排序
        alg_data = alg_data.sort_values('TaskNumber')
        
        # 计算每个柱子的位置
        x = x_positions + i * bar_width - (len(algorithms) - 1) * bar_width / 2
        
        # 为每个TaskNumber创建柱子
        for j, tn in enumerate(task_numbers):
            # 查找当前TaskNumber的数据
            row = alg_data[alg_data['TaskNumber'] == tn]
            if len(row) == 0:
                continue
                
            rate = row['BudgetUsage_Rate'].values[0]
            color = colors[algorithm]
            
            # 计算柱子高度和位置
            min_val = min(rate, 100)
            max_val = max(rate, 100)
            height = max_val - min_val
            
            # 只绘制min(rate,100)到max(rate,100)的部分
            ax.bar(x[j], height, bar_width, 
                   bottom=min_val, color=color, alpha=0.8, label=algorithm if j == 0 else "")
            
            # 添加数值标签
            label_y = rate + 1 if rate > 100 else rate - 1
            va = 'bottom' if rate > 100 else 'top'
            ax.text(x[j], label_y, f'{rate:.1f}%', 
                   ha='center', va=va, fontsize=9, fontweight='bold',
                   bbox=dict(boxstyle='round,pad=0.2', facecolor='white', alpha=0.8))
    
    # 添加100%基准线
    ax.axhline(y=100, color='red', linestyle='--', linewidth=2, alpha=0.7, 
               label='Budget Baseline (100%)')
    
    # 设置标题和标签
    plt.title(f'Budget Usage Deviation by Recipe {recipe}\n(Only Showing Deviation from 100%)', 
              fontsize=14, fontweight='bold')
    plt.xlabel('TaskNumber', fontsize=12)
    plt.ylabel('Budget Usage Rate (%)', fontsize=12)
    plt.ylim(70,130)
    
    # 设置x轴标签
    plt.xticks(x_positions, task_numbers)
    
    # 添加图例 - 只显示一次每个算法
    handles, labels = ax.get_legend_handles_labels()
    by_label = dict(zip(labels, handles))
    plt.legend(by_label.values(), by_label.keys(), 
               bbox_to_anchor=(1.05, 1), loc='upper left')
    
    # 添加网格线
    ax.grid(True, axis='y', alpha=0.3, linestyle='-', linewidth=0.5)
    
    # 调整布局
    plt.tight_layout()
    
    # 显示图表
    plt.show()

# 打印统计结果
print("预算使用率统计:")
print(BudgetUsage_stats.to_string(index=False, float_format='%.2f'))

# 额外：创建汇总偏差图
print("\n创建汇总偏差图...")
fig, ax = plt.subplots(figsize=(14, 8))

# 获取所有算法
all_algorithms = BudgetUsage_stats['Algorithm'].unique()
all_colors = {alg: palette[i % len(palette)] for i, alg in enumerate(all_algorithms)}

# 计算合适的Y轴范围
all_max_rate = BudgetUsage_stats['BudgetUsage_Rate'].max()
all_min_rate = BudgetUsage_stats['BudgetUsage_Rate'].min()
all_y_min = min(all_min_rate, 100) - 5
all_y_max = max(all_max_rate, 100) + 5

# 按Recipe和Algorithm分组计算平均预算使用率
summary_data = BudgetUsage_stats.groupby(['Recipe', 'Algorithm'])['BudgetUsage_Rate'].mean().reset_index()

# 绘制汇总偏差图
recipes_sorted = sorted(summary_data['Recipe'].unique())
x_positions = np.arange(len(recipes_sorted))
bar_width = 0.8 / len(all_algorithms)

for i, algorithm in enumerate(all_algorithms):
    alg_data = summary_data[summary_data['Algorithm'] == algorithm]
    
    # 确保数据按Recipe排序
    alg_data = alg_data.sort_values('Recipe')
    
    # 计算每个柱子的位置
    x = x_positions + i * bar_width - (len(all_algorithms) - 1) * bar_width / 2
    
    for j, recipe in enumerate(recipes_sorted):
        # 查找当前Recipe的数据
        row = alg_data[alg_data['Recipe'] == recipe]
        if len(row) == 0:
            continue
            
        rate = row['BudgetUsage_Rate'].values[0]
        color = all_colors[algorithm]
        
        # 计算柱子高度和位置
        min_val = min(rate, 100)
        max_val = max(rate, 100)
        height = max_val - min_val
        
        # 只绘制min(rate,100)到max(rate,100)的部分
        ax.bar(x[j], height, bar_width, 
               bottom=min_val, color=color, alpha=0.8, label=algorithm if j == 0 else "")
        
        # 添加数值标签
        label_y = rate + 1 if rate > 100 else rate - 1
        va = 'bottom' if rate > 100 else 'top'
        ax.text(x[j], label_y, f'{rate:.1f}%', 
               ha='center', va=va, fontsize=8,
               bbox=dict(boxstyle='round,pad=0.2', facecolor='white', alpha=0.8))

# 添加100%基准线
ax.axhline(y=100, color='red', linestyle='--', linewidth=2, alpha=0.7, 
           label='Budget Baseline (100%)')

# 设置标题和标签
plt.title('Overall Budget Usage Deviation by Recipe and Algorithm\n(Only Showing Deviation from 100%)', 
          fontsize=14, fontweight='bold')
plt.xlabel('Recipe', fontsize=12)
plt.ylabel('Budget Usage Rate (%)', fontsize=12)
plt.ylim(50, 130)

# 设置x轴标签
plt.xticks(x_positions, recipes_sorted)

# 添加图例
handles, labels = ax.get_legend_handles_labels()
by_label = dict(zip(labels, handles))
plt.legend(by_label.values(), by_label.keys(), 
           bbox_to_anchor=(1.05, 1), loc='upper left')

# 添加网格线
ax.grid(True, axis='y', alpha=0.3, linestyle='-', linewidth=0.5)

plt.tight_layout()
plt.show()